commonlibsse_ng\re\c/
Color.rs

1use crate::re::NiColor::NiColor;
2use core::fmt;
3use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
4
5// NOTE: Color needs `Copy` to simplify the four arithmetic operations.
6
7/// Represents an RGBA color with 8-bit channels.
8#[repr(C)]
9#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
10pub struct Color {
11    pub red: u8,
12    pub green: u8,
13    pub blue: u8,
14    pub alpha: u8,
15}
16const _: () = assert!(core::mem::size_of::<Color>() == 0x4);
17
18impl Color {
19    /// Creates a new `Color` with the specified RGBA values.
20    #[inline]
21    pub const fn new(red: u8, green: u8, blue: u8, alpha: u8) -> Self {
22        Self { red, green, blue, alpha }
23    }
24
25    /// Creates a `Color` from a 32-bit hex value (0xRRGGBB).
26    #[inline]
27    pub const fn from_hex(hex: u32) -> Self {
28        Self {
29            red: ((hex >> 16) & 0xFF) as u8,
30            green: ((hex >> 8) & 0xFF) as u8,
31            blue: (hex & 0xFF) as u8,
32            alpha: 0,
33        }
34    }
35
36    /// Converts the color to a 32-bit integer (0xRRGGBBAA).
37    #[inline]
38    pub const fn to_u32(&self) -> u32 {
39        ((self.red as u32) << 24)
40            | ((self.green as u32) << 16)
41            | ((self.blue as u32) << 8)
42            | (self.alpha as u32)
43    }
44
45    /// Converts the color to a hex string in the format `#RRGGBBAA`.
46    pub fn to_hex_string(&self) -> String {
47        format!("#{:02X}{:02X}{:02X}{:02X}", self.red, self.green, self.blue, self.alpha)
48    }
49
50    /// Returns the color components as a slice.
51    #[inline]
52    pub const fn as_slice(&self) -> [u8; 4] {
53        [self.red, self.green, self.blue, self.alpha]
54    }
55}
56
57impl fmt::Display for Color {
58    // Formats the color as `#RRGGBBAA` in hexadecimal.
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        write!(f, "#{:02X}{:02X}{:02X}{:02X}", self.red, self.green, self.blue, self.alpha)
61    }
62}
63
64// --- Operator Overloads ---
65
66impl Add for Color {
67    type Output = Self;
68
69    #[inline]
70    fn add(self, rhs: Self) -> Self::Output {
71        Self {
72            red: self.red.saturating_add(rhs.red),
73            green: self.green.saturating_add(rhs.green),
74            blue: self.blue.saturating_add(rhs.blue),
75            alpha: self.alpha.saturating_add(rhs.alpha),
76        }
77    }
78}
79
80impl AddAssign for Color {
81    #[inline]
82    fn add_assign(&mut self, rhs: Self) {
83        self.red = self.red.saturating_add(rhs.red);
84        self.green = self.green.saturating_add(rhs.green);
85        self.blue = self.blue.saturating_add(rhs.blue);
86        self.alpha = self.alpha.saturating_add(rhs.alpha);
87    }
88}
89
90impl Sub for Color {
91    type Output = Self;
92
93    #[inline]
94    fn sub(self, rhs: Self) -> Self::Output {
95        Self {
96            red: self.red.saturating_sub(rhs.red),
97            green: self.green.saturating_sub(rhs.green),
98            blue: self.blue.saturating_sub(rhs.blue),
99            alpha: self.alpha.saturating_sub(rhs.alpha),
100        }
101    }
102}
103
104impl SubAssign for Color {
105    #[inline]
106    fn sub_assign(&mut self, rhs: Self) {
107        self.red = self.red.saturating_sub(rhs.red);
108        self.green = self.green.saturating_sub(rhs.green);
109        self.blue = self.blue.saturating_sub(rhs.blue);
110        self.alpha = self.alpha.saturating_sub(rhs.alpha);
111    }
112}
113
114impl Mul for Color {
115    type Output = Self;
116
117    #[inline]
118    fn mul(self, rhs: Self) -> Self::Output {
119        Self {
120            red: self.red.saturating_mul(rhs.red),
121            green: self.green.saturating_mul(rhs.green),
122            blue: self.blue.saturating_mul(rhs.blue),
123            alpha: self.alpha.saturating_mul(rhs.alpha),
124        }
125    }
126}
127
128impl MulAssign for Color {
129    #[inline]
130    fn mul_assign(&mut self, rhs: Self) {
131        self.red = self.red.saturating_mul(rhs.red);
132        self.green = self.green.saturating_mul(rhs.green);
133        self.blue = self.blue.saturating_mul(rhs.blue);
134        self.alpha = self.alpha.saturating_mul(rhs.alpha);
135    }
136}
137
138impl Div for Color {
139    type Output = Self;
140
141    #[inline]
142    fn div(self, rhs: Self) -> Self::Output {
143        Self {
144            red: self.red.saturating_div(rhs.red.max(1)), // Avoid division by zero
145            green: self.green.saturating_div(rhs.green.max(1)),
146            blue: self.blue.saturating_div(rhs.blue.max(1)),
147            alpha: self.alpha.saturating_div(rhs.alpha.max(1)),
148        }
149    }
150}
151
152impl DivAssign for Color {
153    #[inline]
154    fn div_assign(&mut self, rhs: Self) {
155        self.red = self.red.saturating_div(rhs.red.max(1));
156        self.green = self.green.saturating_div(rhs.green.max(1));
157        self.blue = self.blue.saturating_div(rhs.blue.max(1));
158        self.alpha = self.alpha.saturating_div(rhs.alpha.max(1));
159    }
160}
161
162// --- Scalar Operations ---
163
164impl Mul<u8> for Color {
165    type Output = Self;
166
167    #[inline]
168    fn mul(self, scalar: u8) -> Self::Output {
169        Self {
170            red: self.red.saturating_mul(scalar),
171            green: self.green.saturating_mul(scalar),
172            blue: self.blue.saturating_mul(scalar),
173            alpha: self.alpha.saturating_mul(scalar),
174        }
175    }
176}
177
178impl Div<u8> for Color {
179    type Output = Self;
180
181    #[inline]
182    fn div(self, scalar: u8) -> Self::Output {
183        Self {
184            red: self.red.saturating_div(scalar.max(1)),
185            green: self.green.saturating_div(scalar.max(1)),
186            blue: self.blue.saturating_div(scalar.max(1)),
187            alpha: self.alpha.saturating_div(scalar.max(1)),
188        }
189    }
190}
191
192impl From<NiColor> for Color {
193    #[inline]
194    fn from(value: NiColor) -> Self {
195        Self { red: value.red as u8, green: value.green as u8, blue: value.blue as u8, alpha: 0 }
196    }
197}
198
199#[test]
200fn test_color_operations() {
201    let color1 = Color::new(100, 150, 200, 255);
202    let color2 = Color::new(50, 50, 50, 128);
203
204    assert_eq!(color1 + color2, Color::new(150, 200, 250, 255));
205    assert_eq!(color1 - color2, Color::new(50, 100, 150, 127));
206    assert_eq!(color1 * color2, Color::new(255, 255, 255, 255));
207    assert_eq!(color1 / color2, Color::new(2, 3, 4, 1));
208
209    // Scalar
210    assert_eq!(color1 * 2, Color::new(200, 255, 255, 255));
211    assert_eq!(color1 / 2, Color::new(50, 75, 100, 127));
212}